package com.superhero.miniprogram.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.superhero.common.exception.BusinessException;
import com.superhero.common.pojo.PageInput;
import com.superhero.common.pojo.entity.AccessRegistration;
import com.superhero.common.pojo.entity.AuthUser;
import com.superhero.common.pojo.entity.UserHealthInfo;
import com.superhero.common.pojo.vo.HealthInfoVo;
import com.superhero.common.pojo.vo.InspectorEnd.AccessInfoInput;
import com.superhero.common.pojo.vo.InspectorEnd.HealthInfoListVo;
import com.superhero.common.pojo.vo.InspectorEnd.InspectorInfo;
import com.superhero.common.pojo.vo.PageVo;
import com.superhero.common.pojo.vo.website.AccessStatisticVo;
import com.superhero.common.pojo.vo.website.NumberVo;
import com.superhero.common.pojo.vo.website.StaticObejct;
import com.superhero.common.pojo.vo.website.TemperatureStatisticVo;
import com.superhero.common.repository.AccessRegistrationRepository;
import com.superhero.common.repository.AuthUserRepository;
import com.superhero.common.repository.UserHealthInfoRepository;
import com.superhero.common.utils.DataMaskUtils;
import com.superhero.common.utils.ExcelUtils;
import com.superhero.common.utils.OptionalUtil;
import com.superhero.common.utils.StaticsUtil;
import com.superhero.miniprogram.security.UserUtil;
import com.superhero.miniprogram.service.InspectorService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.servlet.http.HttpServletResponse;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
public class InspectorServiceImpl implements InspectorService {

    @Autowired
    AuthUserRepository authUserRepository;

    @Autowired
    UserHealthInfoRepository userHealthInfoRepository;

    @Autowired
    AccessRegistrationRepository accessRegistrationRepository;

    @Resource
    BCryptPasswordEncoder passwordEncoder;

    // 扫码获取用户基本信息
    @Override
    public HealthInfoVo getHealthQRCode(Integer id) {
        Optional<AuthUser> byId = authUserRepository.findById(id);
        if (!byId.isPresent()) {
            throw new BusinessException("找不到该微信用户！");
        }
        AuthUser authUser = authUserRepository.findById(id).get();
        UserHealthInfo userHealthInfo = authUser.getUserHealthInfo();
        // 用户还没有填写健康登记表
        if (userHealthInfo == null) {
            userHealthInfo = new UserHealthInfo();
        }
        HealthInfoVo healthInfoVo = new HealthInfoVo();
        BeanUtils.copyProperties(authUser, healthInfoVo);
        BeanUtils.copyProperties(userHealthInfo, healthInfoVo);

        return healthInfoVo;
    }

    // 登记出入人员信息
    @Override
    @Transactional(rollbackFor = Exception.class)
    public AccessRegistration createAccessInfo(Integer userId, AccessInfoInput input) {

        AuthUser authUser = OptionalUtil.get(authUserRepository.findById(userId));
        Double temperature = input.getBodyTemperature();
        UserHealthInfo userHealthInfo = authUser.getUserHealthInfo();
        if (temperature >= 36.6 && temperature <= 37.5) {
            userHealthInfo.setNormalTemperature(1);
        } else {
            userHealthInfo.setNormalTemperature(0);
        }
        userHealthInfoRepository.save(userHealthInfo);

        AccessRegistration access = new AccessRegistration();
        BeanUtils.copyProperties(input, access);
        access.setUser(authUser);
        AuthUser inspecter = OptionalUtil.get(authUserRepository.findById(UserUtil.getUserId()));
        access.setInspector(inspecter);
        int role = authUser.getRole();
        if (role == 1) {
            access.setHousehold(1);
        } else {
            access.setHousehold(0);
        }

        return accessRegistrationRepository.save(access);
    }

    // 获取进入人员出入信息统计
    @Override
    public AccessStatisticVo getAccessStatistic() {
        AccessStatisticVo access = new AccessStatisticVo();
        // access---set
        List<UserHealthInfo> all = userHealthInfoRepository.findAll();
        List<Integer> totals = StaticsUtil.get3Totals(all);
        access.setTotalHousehold(totals.get(0));
        access.setTotalConfirmed(totals.get(1));
        access.setTotalSuspected(totals.get(2));

        StaticObejct household = new StaticObejct();
        StaticObejct visiter = new StaticObejct();
        // StaticObejct --- set --- x_time
        List x_time = StaticsUtil.getPre7Days();
        household.setX_time(x_time);
        visiter.setX_time(x_time);
        // 均7个数据
        int[] householdInList = new int[7];
        int[] householdOutList = new int[7];
        int[] visiterInList = new int[7];
        int[] visiterOutList = new int[7];

        List<AccessRegistration> inOneWeekList = accessRegistrationRepository.findAllInOneWeek();
        for (AccessRegistration daily : inOneWeekList) {
            DateFormat format = new SimpleDateFormat("yyyy/MM/dd");
            String createTime = format.format(daily.getCreateTime());
            int i = x_time.indexOf(createTime); // 找出该登记表是第几天
            if (i == -1) continue;
            if (daily.getHousehold() == 0) {    // 访客
                if (daily.getInOrOut() == 0) {  // 0是进入小区
                    visiterInList[i]++;
                } else {
                    visiterOutList[i]++;
                }
            } else {                            // 住户
                if (daily.getInOrOut() == 0) {  // 0是进入小区
                    householdInList[i]++;
                } else {
                    householdOutList[i]++;
                }
            }
        }
        // StaticObejct --- set--- chartData ----- visiter
        ArrayList<NumberVo> visitChartData = new ArrayList<>();
        for (int i = 0; i < 7; i++) {   // 7 days
            NumberVo numberVo = new NumberVo();
            // visiter
            numberVo.setPeople_type(0);
            ArrayList<Integer> y_value = new ArrayList<>();
            y_value.add(visiterInList[i]);  // in
            y_value.add(visiterOutList[i]); // out
            numberVo.setY_value(y_value);

            visitChartData.add(numberVo);
        }
        // chartData ----- household
        ArrayList<NumberVo> householdChartData = new ArrayList<>();
        for (int i = 0; i < 7; i++) {   // 7 days
            NumberVo numberVo = new NumberVo();
            // household
            numberVo.setPeople_type(1);
            ArrayList<Integer> y_value = new ArrayList<>();
            y_value.add(householdInList[i]);  // in
            y_value.add(householdOutList[i]); // out
            numberVo.setY_value(y_value);

            householdChartData.add(numberVo);
        }
        // StaticObejct --- set ---- chartData
        household.setChartData(householdChartData);
        visiter.setChartData(visitChartData);
        // access---set
        access.setHousehold(household);
        access.setVisiter(visiter);

        return access;
    }

    // 获取进入人员体温信息统计
    @Override
    public TemperatureStatisticVo getTemperatureStatistic() {
        int normal = 0;
        int over = 0;
        int under = 0;
        Double bodyTemperature;
        List<AccessRegistration> all = accessRegistrationRepository.findAll();
        for (AccessRegistration one : all) {
            bodyTemperature = one.getBodyTemperature();
            if (bodyTemperature > 37.5) {
                over++;
            } else if (bodyTemperature >= 36.6) {
                normal++;
            } else {
                under++;
            }
        }
        TemperatureStatisticVo temperatureStatisticVo = new TemperatureStatisticVo();
        temperatureStatisticVo.setOver(over);
        temperatureStatisticVo.setNormal(normal);
        temperatureStatisticVo.setUnder(under);
        temperatureStatisticVo.setOverPercentage((1.0 * over) / (over + normal + under));
        temperatureStatisticVo.setNormalPercentage((1.0 * normal) / (over + normal + under));
        temperatureStatisticVo.setUnderPercentage((1.0 * under) / (over + normal + under));

        return temperatureStatisticVo;
    }

    @Override
    public PageVo getUserHealthInfoList(PageInput pageInput, Integer household, String keyword) {
        Pageable pageable = PageRequest.of(pageInput.getPageNumber(), pageInput.getPageSize());

        // 1. 筛选出相应的数据
        Page<AccessRegistration> accessRegistrations = accessRegistrationRepository.findAll(((root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicates = exportAccessListPredicate(root, criteriaBuilder, keyword, household);
            Predicate[] p = new Predicate[predicates.size()];
            criteriaQuery.orderBy(criteriaBuilder.desc(root.get("createTime")));
            return criteriaBuilder.and(predicates.toArray(p));
        }), pageable);

        // 2. 封装成对应的vo
        PageVo result = PageVo.convert(accessRegistrations);
        result.setContent(accessRegistrations.getContent().stream()
                .map(this::acccessRegistrationConvertToHealthInfoListVo).collect(Collectors.toList()));
        return result;
    }

    @Override
    public ExcelWriter exportUserHealthInfoList(String keyword, Integer household, HttpServletResponse response) {
        // 获取当前用户的所有出入记录
        // 1. 筛选出相应的数据
        List<AccessRegistration> accessRegistrations = accessRegistrationRepository.findAll(((root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicates = exportAccessListPredicate(root, criteriaBuilder, keyword, household);
            Predicate[] p = new Predicate[predicates.size()];
            criteriaQuery.orderBy(criteriaBuilder.desc(root.get("createTime")));
            return criteriaBuilder.and(predicates.toArray(p));
        }));

        // 2.转换为封装的vo
        List<HealthInfoListVo> healthInfoListVos = accessRegistrations.stream().map(this::acccessRegistrationConvertToHealthInfoListVo).collect(Collectors.toList());

        if (!CollectionUtil.isEmpty(healthInfoListVos)) {
            // 3. 设置模板头顺序
            ExcelWriter writer = getExportAccessListExcelWriter();
            // 4. 进行写入
            return ExcelUtils.doExcelWriter(null, writer, healthInfoListVos, HealthInfoListVo.class);
        }
        return null;
    }

    // 获取检查人员基本信息
    @Override
    public InspectorInfo getInspectorInfo() {
        String username = UserUtil.getUserUsername();
        AuthUser authUser = OptionalUtil.get(authUserRepository.findAuthUserByUsername(username));
        InspectorInfo inspectorInfo = new InspectorInfo();
        inspectorInfo.setUsername(authUser.getUsername());
        inspectorInfo.setRealName(authUser.getRealName());
        inspectorInfo.setIdCard(authUser.getIdCard());
        inspectorInfo.setPhone(authUser.getPhone());

        return inspectorInfo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void changePassword(String username, String oldPassword, String newPassword) {
        AuthUser authUser = OptionalUtil.get(authUserRepository.findAuthUserByUsername(username));
        boolean isMatch = passwordEncoder.matches(oldPassword, authUser.getPassword());
        if (!isMatch) {
            throw new BusinessException("您输入的旧密码错误！");
        }
        authUser.setPassword(passwordEncoder.encode(newPassword));
        authUserRepository.saveAndFlush(authUser);
    }

    private List<Predicate> exportAccessListPredicate(Root<AccessRegistration> root, CriteriaBuilder criteriaBuilder, String keyword, Integer household) {
        List<Predicate> predicates = new ArrayList<>();

        predicates.add(criteriaBuilder.equal(root.join("user").get("locked"), "0"));
        predicates.add(criteriaBuilder.equal(root.get("household"), household));
        if (!StringUtils.isEmpty(keyword)) {
            String key = "%" + keyword.trim() + "%";
            String upperKey = "%" + keyword.toUpperCase().trim() + "%";
            // key可为人员姓名、检查人员姓名、出入类型（出/入），检查人员手机号，社区名称的全模糊查询
            Predicate p1 = null, p2 = null, p3 = null, p4 = null, p5 = null;
            p1 = criteriaBuilder.like(criteriaBuilder.upper(root.join("user").get("username")), upperKey);
            p2 = criteriaBuilder.like(criteriaBuilder.upper(root.join("inspector").get("username")), upperKey);
            p4 = criteriaBuilder.like(root.join("inspector").get("phone"), key);
            p5 = criteriaBuilder.like(root.join("user").get("userHealthInfo").get("communityLocation"), upperKey);
            if ("出".equals(keyword.trim())) {
                p3 = criteriaBuilder.equal(root.get("inOrOut").as(Integer.class), 0);
                predicates.add(criteriaBuilder.or(p1, p2, p3, p4, p5));
            } else if ("入".equals(keyword.trim())) {
                p3 = criteriaBuilder.equal(root.get("inOrOut").as(Integer.class), 1);
                predicates.add(criteriaBuilder.or(p1, p2, p3, p4, p5));
            } else {
                predicates.add(criteriaBuilder.or(p1, p2, p4, p5));
            }
        }
        return predicates;
    }

    private HealthInfoListVo acccessRegistrationConvertToHealthInfoListVo(AccessRegistration accessRegistration) {
        HealthInfoListVo healthInfoListVo = new HealthInfoListVo();
        BeanUtils.copyProperties(accessRegistration, healthInfoListVo);
        healthInfoListVo.setName(accessRegistration.getUser().getRealName());
        healthInfoListVo.setInspectorName(accessRegistration.getInspector().getRealName());
        healthInfoListVo.setInspectorPhone(accessRegistration.getInspector().getPhone());
        healthInfoListVo.setInspectorPhone(DataMaskUtils.maskPhone(healthInfoListVo.getInspectorPhone()));
        if (accessRegistration.getUser().getUserHealthInfo() != null) {
            UserHealthInfo userHealthInfo = accessRegistration.getUser().getUserHealthInfo();
            healthInfoListVo.setCommunityLocation(userHealthInfo.getCommunityLocation());
            healthInfoListVo.setPersonType(userHealthInfo.getPersonType());
        }
        return healthInfoListVo;
    }

    private ExcelWriter getExportAccessListExcelWriter() {
        ExcelWriter writer = new ExcelWriter(true);
        writer.addHeaderAlias("personType", "人员性质（1-常住 2-租户 3-访客）");
        writer.addHeaderAlias("name", "人员姓名");
        writer.addHeaderAlias("inOrOut", "出入类型（0-进入小区，1-离开小区）");
        writer.addHeaderAlias("createTime", "出入时间");
        writer.addHeaderAlias("bodyTemperature", "体温");
        writer.addHeaderAlias("symptomDetail", "详细异常症状");
        writer.addHeaderAlias("inspectorName", "检查人员姓名");
        writer.addHeaderAlias("inspectorPhone", "检查人员联系方式");
        writer.addHeaderAlias("communityLocation", "社区名称");
        return writer;
    }

}
